package com.symaster.backups.frame.element.menu;

import com.symaster.backups.util.AnimationUtil;
import com.symaster.backups.util.TimeUtil;
import com.symaster.common.animation.Curve;

import java.awt.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author yinmiao
 * @version 2023/12/10
 */
public class TopMenuLayout implements LayoutManager {

    static final Curve curve = Curve.TYPE_FAST_TO_SLOW;
    static final float animationTime = 150;
    static final float accuracy = 0.000000001f; // 曲线计算精度

    @Override
    public void addLayoutComponent(String name, Component comp) {

    }

    @Override
    public void removeLayoutComponent(Component comp) {

    }

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        synchronized (parent.getTreeLock()) {
            int preferredHeight = Arrays.stream(parent.getComponents()).mapToInt(e -> e.getPreferredSize().height).max().orElse(0);

            Container parent1 = parent.getParent();
            if (parent1 == null) {
                return new Dimension(50, preferredHeight);
            } else {
                return new Dimension(parent1.getWidth(), preferredHeight);
            }
        }
    }

    @Override
    public Dimension minimumLayoutSize(Container parent) {
        return new Dimension(1, 1);
    }

    @Override
    public void layoutContainer(Container parent) {
        if (parent instanceof TopMenu topMenu) {

            int height = parent.getHeight();

            Component bar;
            List<MenuItem> menuItems;
            Component[] components = topMenu.getComponents();
            if (components != null && components.length > 0 &&
                    (bar = getBar(components)) != null &&
                    !((menuItems = getMenuItems(components)).isEmpty())) {

                TopMenu.IndexData indexData = topMenu.indexData;
                TopMenu.MotionData motionData = topMenu.motionData;

                layoutMenuItems(menuItems, height);

                layoutBar(topMenu, menuItems, indexData, bar);
            }
        }
    }

    private static void layoutBar(TopMenu topMenu, List<MenuItem> menuItems, TopMenu.IndexData indexData, Component bar) {
        Map<Integer, MenuItem> byIndex = menuItems.stream().collect(Collectors.toMap(MenuItem::getIndex, e -> e));

        MenuItem menuItem = byIndex.get(indexData.selectIndex);
        if (menuItem == null) {
            throw new NullPointerException("Get MenuItem By Index Is Empty");
        }
        MenuItem newMenuItem = byIndex.get(indexData.selectNewIndex);
        if (newMenuItem == null) {
            throw new NullPointerException("Get MenuItem By Index Is Empty");
        }

        if (indexData.selectIndex == indexData.selectNewIndex) {
            bar.setBounds(menuItem.getX(), topMenu.getHeight() - topMenu.barHeight, menuItem.getWidth(), topMenu.barHeight);
        } else {
            float timeCom = TimeUtil.floatTime() - indexData.selectIndexCheckStartTime;
            float point = (float) AnimationUtil.getPoint(curve, Math.min(timeCom / animationTime, 1f));

            float srcPoint = menuItem.getX() + menuItem.getWidth() / 2f;
            float dstPint = newMenuItem.getX() + newMenuItem.getWidth() / 2f;

            int srcWidth = menuItem.getWidth();
            int dstWidth = newMenuItem.getWidth();

            float thatPoint = point * (dstPint - srcPoint) + srcPoint;
            float thatWidth = point * (dstWidth - srcWidth) + srcWidth;

            bar.setBounds(Math.round(thatPoint - thatWidth / 2f), topMenu.getHeight() - topMenu.barHeight, Math.round(thatWidth), topMenu.barHeight);

            if (timeCom > animationTime) {
                indexData.selectIndex = indexData.selectNewIndex;
            }
        }
    }

    private void layoutMenuItems(List<MenuItem> menuItems, int height) {
        int xOffset = 0;
        for (MenuItem menuItem : menuItems) {
            int width = menuItem.getPreferredSize().width;
            menuItem.setBounds(xOffset, 0, width, height - 4);
            xOffset += width;
        }
    }

    private List<MenuItem> getMenuItems(Component[] components) {
        return Arrays.stream(components)
                .filter(e -> e instanceof MenuItem)
                .map(e -> ((MenuItem) e))
                .sorted(Comparator.comparingInt(MenuItem::getIndex))
                .collect(Collectors.toList());
    }

    private Component getBar(Component[] components) {
        for (Component component : components) {
            if (component instanceof Bar) {
                return component;
            }
        }
        return null;
    }
}
